import { Matrix4 } from "../../utils/Matrix4";
import { Primitives } from "../../utils/Primitives";
import { Util } from "../../utils/Util";
import { WebGLUtil } from "../../utils/WebGLUtil";
import { Render } from "../Render";
import { WebglTemplateBase } from "./WebglTemplateBase";

/*
 * @Author: Snow
 * @Date: 2021-10-13 16:04:28
 * @Description:可视化相机（同一个屏幕观察相机视角）
 */
export class TemplateLookCamera extends WebglTemplateBase {
    public static debug = true;
    /* 相机程序信息 */
    private cameraProgramInfo: {
        program: WebGLProgram,
        uniformSetters: { [name: string]: Function },
        attribSetters: { [name: string]: Function },
    };

    protected override initUITools(): void {
        this.programInfo
        const gl = Render.GL;
        let translation = [200, 150];
        let scale = [1, 1];
        this.uiParam = {
            rotation: 150,  // 以角度为单位
            cam1FieldOfView: 60,  // 以角度为单位
            cam1PosX: 0,
            cam1PosY: 0,
            cam1PosZ: -200,
            cam1Near: 30,
            cam1Far: 500,
            cam1Ortho: true,  // 正交矩阵
            cam1OrthoUnits: 120,
        }
        // Setup a ui.
        this.createSlider("rotation", { value: this.uiParam.rotation, slide: this.onUpateSlide(this, "rotation"), min: 0, max: 360, step: 1, precision: 0 })
        this.createSlider("cam1FieldOfView", { value: this.uiParam.cam1FieldOfView, slide: this.onUpateSlide(this, "cam1FieldOfView"), min: 0, max: 180, step: 1, precision: 0 })
        this.createSlider("cam1PosX", { value: this.uiParam.cam1PosX, slide: this.onUpateSlide(this, "cam1PosX"), min: -200, max: 200, step: 1, precision: 0 })
        this.createSlider("cam1PosY", { value: this.uiParam.cam1PosY, slide: this.onUpateSlide(this, "cam1PosY"), min: -200, max: 200, step: 1, precision: 0 })
        this.createSlider("cam1PosZ", { value: this.uiParam.cam1PosZ, slide: this.onUpateSlide(this, "cam1PosZ"), min: -200, max: 200, step: 1, precision: 0 })
        this.createSlider("cam1Near", { value: this.uiParam.cam1Near, slide: this.onUpateSlide(this, "cam1Near"), min: 1, max: 100, step: 1, precision: 0 })
        this.createSlider("cam1Near", { value: this.uiParam.cam1Near, slide: this.onUpateSlide(this, "cam1Far"), min: 101, max: 2000, step: 1, precision: 0 })
        this.createTabel("cam1Ortho", "正交矩阵",true);
        this.createSlider("cam1OrthoUnits", { value: this.uiParam.cam1OrthoUnits, slide: this.onUpateSlide(this, "cam1OrthoUnits"), min: 1, max: 150, step: 1, precision: 0 })
    }

    protected override initProgram(): void {
        this.vsSource = `
            attribute vec4 a_position;
            attribute vec4 a_color;
            
            uniform mat4 u_matrix;
            
            varying vec4 v_color;
            
            void main() {
                // Multiply the position by the matrix.
                gl_Position = u_matrix * a_position;
                
                // Pass the vertex color to the fragment shader.
                v_color = a_color;
            }
        `;

        this.fsSource = `
            precision mediump float;

            // Passed in from the vertex shader.
            varying vec4 v_color;
            
            void main() {
                gl_FragColor = v_color;
            }
        `;
        super.initProgram();

        /* 相机顶点 */
        const cameraVsSource = `
            attribute vec4 a_position;
    
            uniform mat4 u_matrix;
            
            void main() {
                // 将 position 乘以矩阵
                gl_Position = u_matrix * a_position;
            }
        `;

        const cameraFsSource = `
            precision mediump float;
    
            uniform vec4 u_color;
            
            void main() {
                gl_FragColor = u_color;
            }
        `;
        this.cameraProgramInfo = WebGLUtil.createProgramInfo(Render.GL, cameraVsSource, cameraFsSource);
    }

    protected override initBuffers(): void {
        super.initBuffers();
        const gl = Render.GL;
        gl.enable(gl.CULL_FACE);

        ///裁剪测试
        gl.enable(gl.SCISSOR_TEST);

        // create buffers and fill with data for a 3D 'F'
        const fBufferInfo = Primitives.createBufferInfoFunc(Primitives.create3DFVertices)(gl);


        //let bufferInfo = WebGLUtil.createBufferInfoFromArrays(gl, arrays);
        let cameraAngleRadians = Util.degToRad(0);

        let cameraHeight = 50;

        const cameraBufferInfo = this.createCameraBufferInfo(gl,20);

        const clipspaceCubeBufferInfo = this.createClipspaceCubeBufferInfo(gl);

        let drawScene = () => {
            let time = this.ts;
            time = time * 0.5 + 5;
            WebGLUtil.resizeCanvasToMatchDisplaySize(gl);
            // Clear the canvas AND the depth buffer.
            gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

            // 我们要把视角分成 2 个
            const effectiveWidth = gl.canvas.clientWidth / 2;
            const aspect = effectiveWidth / gl.canvas.clientHeight;
            const near = this.uiParam.cam1Near;
            const far = this.uiParam.cam1Far;
            const fieldOfViewRadians = Util.degToRad(this.uiParam.cam1FieldOfView);
            // Compute the projection matrix

            //是否是正交矩阵
            let cam1Ortho:boolean = this.uiParam.cam1Ortho;  // 正交矩阵
            let cam1OrthoUnits = this.uiParam.cam1OrthoUnits;
            
            const projectionMatrix = cam1Ortho?Matrix4.orthographic(-cam1OrthoUnits * aspect,cam1OrthoUnits * aspect,-cam1OrthoUnits,cam1OrthoUnits,near,far)  : Matrix4.perspective(fieldOfViewRadians, aspect, near, far);

            // draw on the left with orthographic camera
            const height = gl.canvas.height;
            const width = gl.canvas.width;
            const leftWidth: number = width / 2;
            gl.viewport(0, 0, leftWidth, height);
            gl.scissor(0, 0, leftWidth, height);
            gl.clearColor(1, 0.8, 0.8, 1);

            // Compute the camera's matrix using look at.
            let cam1PosX = this.uiParam.cam1PosX;
            let cam1PosY = this.uiParam.cam1PosY;
            let cam1PosZ = this.uiParam.cam1PosZ;
            let cameraPosition = [cam1PosX, cam1PosY, cam1PosZ];
            let target = [0, 0, 0];
            let up = [0, 1, 0];
            const cameraMatrix = Matrix4.lookAt(cameraPosition, target, up);

            // Make a view matrix from the camera matrix.
            const viewMatrix = Matrix4.invert(cameraMatrix);

            let worldMatrix = Matrix4.rotateY(Util.degToRad(this.uiParam.rotation));
            worldMatrix = Matrix4.multiply(worldMatrix, Matrix4.rotateX(Util.degToRad(this.uiParam.rotation)));
            // 使 F 围绕着它的原点
            worldMatrix = Matrix4.multiply(worldMatrix, Matrix4.translation(-35, -75, -5));

            let matrix = Matrix4.multiply(projectionMatrix, viewMatrix);
            matrix = Matrix4.multiply(matrix, worldMatrix);

            gl.useProgram(this.programInfo.program);
            // Setup all the needed buffers and attributes.
            WebGLUtil.setBuffersAndAttributes(gl, this.programInfo, fBufferInfo);

            //uniforms
            WebGLUtil.setUniforms(this.programInfo, {
                u_matrix: matrix,
            });
            WebGLUtil.drawBufferInfo(gl, fBufferInfo);


            /////还是绘制右边内容
            // 使用透视相机绘制在右边
            const rightWidth = width - leftWidth;
            gl.viewport(leftWidth, 0, rightWidth, height);
            gl.scissor(leftWidth, 0, rightWidth, height);
            gl.clearColor(0.8, 0.8, 1, 1);

            //------------------------------>分隔符<------------------------------------------------------//
            // 计算第二个投影矩阵和第二个相机
            ///固定透视视角
            const perspectiveProjectionMatrix2 = Matrix4.perspective(Util.degToRad(60), aspect, 1, 2000);
            // 使用 look at 计算相机的矩阵
            const cameraPosition2 = [-600, 400, -400];
            const target2 = [0, 0, 0];
            const cameraMatrix2 = Matrix4.lookAt(cameraPosition2, target2, up);
            const viewMatrix2 = Matrix4.invert(cameraMatrix2)
            matrix = Matrix4.multiply(perspectiveProjectionMatrix2, viewMatrix2);
            matrix = Matrix4.multiply(matrix, worldMatrix);

            //uniforms
            WebGLUtil.setUniforms(this.programInfo, {
                u_matrix: matrix,
            });
            //画第二个F
            WebGLUtil.drawBufferInfo(gl, fBufferInfo);

            let mat:number[]
            //----------------------------------分割线-----------------------------------//
            // 绘制代表第一个相机的物体
            {
                // 从第 2 个相机矩阵中创建一个视图矩阵
                mat = Matrix4.multiply(perspectiveProjectionMatrix2, viewMatrix2);
                // 使用第一个相机的矩阵作为表示相机的物体的世界矩阵
                mat = Matrix4.multiply(mat, cameraMatrix);

                gl.useProgram(this.cameraProgramInfo.program);

                // ------ 绘制表示相机的物体 --------
                // 设置所有需要的 attributes
                WebGLUtil.setBuffersAndAttributes(gl, this.cameraProgramInfo,cameraBufferInfo);

                // 设置 uniforms
                WebGLUtil.setUniforms(this.cameraProgramInfo, {
                    u_matrix: mat,
                    u_color: [0, 0, 0, 1],
                });

                WebGLUtil.drawBufferInfo(gl, cameraBufferInfo, gl.LINES);
            }
            
            //----------------------------------分割线-----------------------------------//
            //绘制锥体
            {
                // 第一个透视矩阵的逆矩阵做做裁剪区间的转换
                mat = Matrix4.multiply(mat, Matrix4.invert(projectionMatrix));
               
                gl.useProgram(this.cameraProgramInfo.program);

                // ------ 绘制表示相机的物体 --------
                // 设置所有需要的 attributes
                WebGLUtil.setBuffersAndAttributes(gl, this.cameraProgramInfo,clipspaceCubeBufferInfo);

                // 设置 uniforms
                WebGLUtil.setUniforms(this.cameraProgramInfo, {
                    u_matrix: mat,
                    u_color: [0, 0, 0, 1],
                });

                WebGLUtil.drawBufferInfo(gl, clipspaceCubeBufferInfo, gl.LINES);
            }

        }

        this.draw = drawScene;
    }

    /* 加载图片 */
    private loadImageTexture(url: string): WebGLTexture {
        const gl = Render.GL;
        // 创建一个纹理
        const texture = gl.createTexture();
        gl.bindTexture(gl.TEXTURE_2D, texture);
        // 用一个 1x1 蓝色像素填充该纹理
        gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE,
            new Uint8Array([0, 0, 255, 255]));
        // 异步加载一张图片
        const image = new Image();
        image.src = url;
        image.addEventListener('load', function () {
            // 现在图片加载完了，把它拷贝到纹理中
            gl.bindTexture(gl.TEXTURE_2D, texture);
            gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
            // 假设该纹理的宽高是 2 的整次幂
            gl.generateMipmap(gl.TEXTURE_2D);
        });
        return texture;
    }

    // 为一个相机创建几何
    private createCameraBufferInfo(gl: WebGLRenderingContext, scale: number = 1) {
        // 首先，让我们添加一个立方体。它的范围是 1 到 3，
        // 因为相机看向的是 -Z 方向，所以我们想要相机在 Z = 0 处开始。
        // 我们会把一个圆锥放到该立方体的前面，
        // 且该圆锥的开口方向朝 -Z 方向。
        const positions = [
            -1, -1, 1,  // 立方体的顶点
            1, -1, 1,
            -1, 1, 1,
            1, 1, 1,
            -1, -1, 3,
            1, -1, 3,
            -1, 1, 3,
            1, 1, 3,
            0, 0, 1,  // 圆锥的尖头
        ];
        const indices = [
            0, 1, 1, 3, 3, 2, 2, 0, // 立方体的索引
            4, 5, 5, 7, 7, 6, 6, 4,
            0, 4, 1, 5, 3, 7, 2, 6,
        ];
        // 添加圆锥的片段
        const numSegments = 6;
        const coneBaseIndex = positions.length / 3;
        const coneTipIndex = coneBaseIndex - 1;
        for (let i = 0; i < numSegments; ++i) {
            const u = i / numSegments;
            const angle = u * Math.PI * 2;
            const x = Math.cos(angle);
            const y = Math.sin(angle);
            positions.push(x, y, 0);
            // 从圆锥尖头到圆锥边缘的线段
            indices.push(coneTipIndex, coneBaseIndex + i);
            // 从圆锥边缘一点到圆锥边缘下一点的线段
            indices.push(coneBaseIndex + i, coneBaseIndex + (i + 1) % numSegments);
        }

        /* 直接缩放了顶点信息 也可以用缩放矩阵来缩放 效果一样 */
        positions.forEach((v, ndx) => {
            positions[ndx] *= scale;
        });
        return WebGLUtil.createBufferInfoFromArrays(gl, {
            position: positions,
            indices,
        });
    }
    /**
     * @Author: Snow
     * @description: 裁剪空间立方体
     * @param {*} gl
     * @return {*}
     */    
    private createClipspaceCubeBufferInfo(gl:WebGLRenderingContext) {
        // 首先，让我们添加一个立方体。它的范围是 1 到 3，
        // 因为相机看向的是 -Z 方向，所以我们想要相机在 Z = 0 处开始。
        // 我们会把一个圆锥放到该立方体的前面，
        // 且该圆锥的开口方向朝 -Z 方向。
        const positions = [
          -1, -1, -1,  // 立方体的顶点
           1, -1, -1,
          -1,  1, -1,
           1,  1, -1,
          -1, -1,  1,
           1, -1,  1,
          -1,  1,  1,
           1,  1,  1,
        ];
        const indices = [
          0, 1, 1, 3, 3, 2, 2, 0, // 立方体的索引
          4, 5, 5, 7, 7, 6, 6, 4,
          0, 4, 1, 5, 3, 7, 2, 6,
        ];
        return WebGLUtil.createBufferInfoFromArrays(gl, {
          position: positions,
          indices,
        });
      }
}